﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;

using PTTWebSite;

namespace PPTWCF
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class InfoService : IInfoService
    {
        
        PTTEntities1 ents = new PTTEntities1();

        // To use HTTP GET, add [WebGet] attribute. (Default ResponseFormat is WebMessageFormat.Json)
        // To create an operation that returns XML,
        //     add [WebGet(ResponseFormat=WebMessageFormat.Xml)],
        //     and include the following line in the operation body:
        //         WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
    //    [OperationContract]
        public string DoWork()
        {
            // Add your operation implementation here
            return "huffffhh";
        }

        /// <summary>
        /// Removes every position that is older then 4 min. This method should run before every client request
        /// </summary>
        private void RemoveOlderThen4Min()
        {
            System.Diagnostics.Debug.WriteLine("Remove Method Runs");

            DateTime before4min = DateTime.Now.AddMinutes(-4);

            var query = from item in ents.ActualPos
                        where item.insertTime.Value.CompareTo(before4min) < 0
                        select item;            

            foreach (var item in query)
            {
                System.Diagnostics.Debug.WriteLine(String.Format("Iserted: {0}, Lat: {1}, Lon: {2}, VNumber: {3}", item.insertTime,
                    item.latitude, item.longitude, item.vehicleNumber));
            }

            foreach (var item in query)
            {
                ents.DeleteObject(item);
            }

            ents.SaveChanges();
        }

        
        //Inserts a new Position for a specific vehicle
        public void InsertNewPosition(int VNumber, string Vtype, string userid, float lan, float lon)
        {
//            RemoveOlderThen4Min();
       //     String ret = "";
           
            
            DateTime insTime = DateTime.Now;

            if (userid == null)
                throw new Exception("User ID can not be null");


            List<StationListItem> stationsOfThisV =  this.ListStations(VNumber, Vtype);

            bool ok = false;

            foreach (var item in stationsOfThisV)
            {
                if (this.GetEucledianDistance(item.Longiture, item.Latitude, lon, lan) < 1)
                {
                    ok = true;
                }
            }

            if (!ok)
            {
                throw new Exception("Not on the Line!");
            }

           this.updateTimeFromTheLastStationColumn(lan, lon, insTime, userid, VNumber, Vtype);

            ActualPos newPosition =  new ActualPos();            
            newPosition.vehicleNumber = VNumber;
            newPosition.latitude = lan;
            newPosition.longitude = lon;
            newPosition.userID = userid;
            newPosition.vtype = Vtype;

            newPosition.insertTime = insTime;


            ents.ActualPos.AddObject(newPosition);
           
            ents.SaveChanges();

        }

        /// <summary>
        /// Returns the ordered list of the stations
        /// </summary>
        /// <param name="vNumber">The number of the jarat</param>
        /// <returns>The Ordered list</returns>
        public List<StationListItem> ListStations(int vNumber, string vType)
        {
            int?   mv = this.GetIDOfVehicle(vNumber, vType);
            int id;

            if(mv.HasValue)
                id = mv.Value;
            else
                return null;


            var query = from trav in ents.Travels
                        join stion in ents.Stations on trav.stationId equals stion.id
                        where trav.vehicleId == id
                        orderby trav.Stationnumber
                        select new StationListItem
                        {
                            StationName = stion.name,
                            Snumber = trav.Stationnumber,
                            Longiture = stion.longitude,
                            Latitude = stion.latitude,
                            TimeFromTheLastStation = trav.timeFromTheLastInSeconds.Value,
                            StationID = stion.id
                        };

            return query.ToList();
        }

        public List<int> GetLinesByType(String type)
        {
           
            var query = from item in ents.Vehicles
                        where item.vtype == type
                        select item.number;

            return query.ToList();
        }



        public List<string> GetAllStations()
        {
            var query = from item in ents.Stations
                        select item.name;

            return query.ToList();
        }


        public Position GetPositionOfStation(string StationName)
        {

            var selectedStations = from item in ents.Stations
                                   where item.name == StationName
                                   select item;

            if (selectedStations.Count() == 0)
            {
                return null;
            }
            else
            {
                return new Position { Latitude = selectedStations.First().latitude, Longitude = selectedStations.First().longitude };
            }
        }

        /// <summary>
        /// This method returns a Unique ID for a user, so she/he can insert new position.
        /// </summary>
        /// <param name="lat">Actual Latitude of the user</param>
        /// <param name="lon">Actual Longitude of the User</param>
        /// <returns>The Unique ID</returns>
        public String GetID(float lat, float lon)
        {         
            List<ActualPos> ll = (from vs in ents.ActualPos
                                  select vs).ToList();

            DateTime before1Min = DateTime.Now.AddSeconds(-2);


            var query = from item in ll
                        where this.GetEucledianDistance(lon, lat, item.longitude, item.latitude) == 0 && item.insertTime.Value.CompareTo(before1Min) > 0
                        select item;

            if (query.Count() == 0)
                return Guid.NewGuid().ToString();
            else
                return null;
        }


        /// <summary>
        /// returns the database id of a given VehicleNumber (e.g the id of the 49 is 1)
        /// </summary>
        /// <param name="Vname">e.g. 6 (6os villamos..)</param>
        /// <returns></returns>
        private int? GetIDOfVehicle(int Vname, string Vtype)
        {
            var query = from item in ents.Vehicles
                        where item.number == Vname && item.vtype == Vtype
                        select item;

            int? ret = query.First().id;

            if (ret.HasValue)
                return ret.Value;
            else
                return null;
        }

        /// <summary>
        /// Returns how many minutes you have to wait until the next vehicle. It accepts the actual
        /// latitude and longitude. From this we calculate the nearest station, and the the estimated
        /// waiting time between this station and the actual position of the nearest vehicle.
        /// </summary>
        /// <param name="vNumber">The vehicle number (e.g. 6)</param>
        /// <param name="lat">THe actual Latitude</param>
        /// <param name="lon">The actual Longitude</param>
        /// <returns>-1 if we have no information, or the actual waiting time</returns>
        public int getTimeFromStation(int vNumber, string vType , float lat, float lon)
        {
            //Megmondja azokat, akik egyaltalan szoba jonnek vehicleNUmber szerint
            var vNumberItems = from item in ents.ActualPos
                               where item.vehicleNumber == vNumber
                               group item by item.userID;
                                                             

            List<ActualPos> newestPoss = new List<ActualPos>();

            //Ha nincs ilyen az adatbazisban nullt adunk vissza
            if (vNumberItems.Count() == 0)
                return -1;

            //Kiszedi a legkorabbiakat, hiszen a tobbi csak korabbib pozicio
            foreach (var item in vNumberItems)
            {

                var query = (from pos in item
                            orderby pos.insertTime
                            select pos).Last();
  
                newestPoss.Add(query);
            }

            //Kivalasztja azt, amelyikket a legkisebb a tavolsag
            var listedV = from item in newestPoss
                          let ed = GetEucledianDistance(item.longitude, item.latitude, lat, lon)
                          orderby ed
                          select item;

           
          
            //Ehhez a poziciohoz vagyunk a legkozelebb
            float Lat = listedV.First().latitude;
            float Lon= listedV.First().longitude;

            List<StationListItem> stations = this.ListStations(vNumber, vType);

           
            StationListItem nearestfromActV = (from item in stations
                        let dist = GetEucledianDistance(item.Longiture, item.Latitude, Lon, Lat)
                        orderby dist
                        select item).First();


            StationListItem nearestFromUs = (from item in stations
                                            let distance = this.GetEucledianDistance(lon, lat, item.Longiture, item.Latitude)
                                            orderby distance
                                            select item).First();

            int less, bigger;

            if (nearestfromActV.Snumber < nearestFromUs.Snumber)
            {
                less = nearestfromActV.Snumber;
                bigger = nearestFromUs.Snumber;
            }
            else
            {
                less = nearestFromUs.Snumber;
                bigger = nearestfromActV.Snumber;
            }

            var sumquery = from item in stations
                           where item.Snumber >= less && item.Snumber < bigger
                           select item;

            int ret = 0;

            foreach (var item in sumquery)
            {
                ret += item.TimeFromTheLastStation;
            }
            

            return ret;
        }

        /// <summary>
        /// Returns all actual positions
        /// </summary>
        /// <returns></returns>
     //   [OperationContract]
        public List<Position> getAllVehiclePositions()
        {
            List<Position> ret = new List<Position>();

            RemoveOlderThen4Min();


            var query = from item in ents.ActualPos
                        group item by item.userID;

            if (query.Count() == 0)
                return null;

            foreach (var item in query)
            {
                var latests = (from pos in item
                             orderby pos.insertTime
                             select pos).Last();

                ret.Add(new Position {Latitude = latests.latitude, Longitude = latests.longitude, VehicleNumber = latests.vehicleNumber });
            }

            return ret;
        }


        /// <summary>
        /// Returns all Lines from the Database
        /// </summary>
        /// <returns></returns>
  //      [OperationContract]
        public List<int> GetLines()
        {      
            return  (from item in ents.Vehicles
                       select item.number).ToList();
        }


        public List<String> GetLinesWithType()
        {
            List<string> ret = new List<string>();

            var query = from item in ents.Vehicles
                        select item;

            foreach (var item in query)
            {
                String str = item.number.ToString() + " " + item.vtype;
                ret.Add(str);                
            }

            return ret;
        }
   
        /// <summary>
        /// Returns the EucledianDistance between 2 points
        /// </summary>
        /// <param name="pos1lon"></param>
        /// <param name="pos1lat"></param>
        /// <param name="pos2lon"></param>
        /// <param name="pos2lat"></param>
        /// <returns></returns>
        protected float GetEucledianDistance(float pos1lon, float pos1lat, float pos2lon, float pos2lat)
        {
            float ret = -1;

            ret = (float)Math.Sqrt(Math.Pow(pos2lat - pos1lat, 2) + Math.Pow(pos2lon - pos1lon, 2));

            return ret;
        }
        /// <summary>
        /// Should run BEFORE we insert the new position
        /// </summary>
        /// <param name="NewLatitude"></param>
        /// <param name="NewLongitude"></param>
        /// <param name="insertTime"></param>
        /// <param name="guid"></param>
        /// <param name="vNumber"></param>
        public void updateTimeFromTheLastStationColumn(float NewLatitude, float NewLongitude, DateTime insertTime, string guid, int vNumber, string Vtype)
        {
            string ret = "";

            List<Station> Allstations = (from item in ents.Stations
                                      select item).ToList();

            //megnezzuk,h megalloban vagyunk -e
            var stationsQuery = from item in Allstations
                                where this.isInThatPosition(item.latitude, item.longitude, NewLatitude, NewLongitude) // where item.latitude == NewLatitude && item.longitude == NewLongitude
                                select item;
                     
            //Ha nem vagyunk megalloban, visszalepunk
            if (stationsQuery.Count() == 0)
            {

                return;// 0.ToString();
            }
            Station actStation = stationsQuery.First();
            
       
            //Kikeressuk az elozo stationt
            var AllPositions = from item in ents.ActualPos
                               where item.userID == guid
                               select item;

            Station lastStation = null;

            ActualPos lastStationInActPos = null;

            IEnumerable<Station> lastStationQuery;

            foreach (var item in AllPositions)
            {
               lastStationQuery = from stat in Allstations
                                  where this.isInThatPosition(item.latitude, item.longitude, stat.latitude, stat.longitude)
                                  select stat;
              if (lastStationQuery.Count() == 1)
              {
                  lastStation = lastStationQuery.First();
                  lastStationInActPos = item;
              }
            }

            if (lastStation == null)
            {
                this.removeElements(guid);
                return;// null;
            }

            List<Travel> travels = (from item in ents.Travels
                                   select item).ToList();

            Travel ActStationInTravel = (from item in travels
                                        where item.stationId == actStation.id && item.vehicleId == this.GetIDOfVehicle(vNumber, Vtype)
                                        select item).First();

            Travel LastStationinTravel = (from item in travels
                                          where item.stationId == lastStation.id && item.vehicleId == this.GetIDOfVehicle(vNumber, Vtype)
                                          select item).First();

            ret = ActStationInTravel.Stationnumber + " " + LastStationinTravel.Stationnumber;

            if (ActStationInTravel.Stationnumber > LastStationinTravel.Stationnumber)
            {
                Travel t = (from item in ents.Travels
                            where item.vehicleId == ActStationInTravel.vehicleId && item.stationId == ActStationInTravel.stationId &&
                            item.Stationnumber == ActStationInTravel.Stationnumber
                            select item).First();

                t.timeFromTheLastInSeconds =  (t.timeFromTheLastInSeconds + (int)insertTime.Subtract(lastStationInActPos.insertTime.Value).TotalSeconds)/2;
            }
            else
            {
                Travel t = (from item in ents.Travels
                            where item.vehicleId == LastStationinTravel.vehicleId && item.stationId == LastStationinTravel.stationId &&
                            item.Stationnumber == LastStationinTravel.Stationnumber
                            select item).First();

                t.timeFromTheLastInSeconds = (t.timeFromTheLastInSeconds + (int)insertTime.Subtract(lastStationInActPos.insertTime.Value).TotalSeconds)/2;
            }

            //--remove the old entries from the actual user            

            ents.SaveChanges();

            this.removeElements(guid);

           // return ret;
        }



        private bool isInThatPosition(float P1Lat, float P1Lon, float P2Lat, float P2Lon)
        {
            return ((P1Lat.ToString() == P2Lat.ToString()) && (P1Lon.ToString() == P2Lon.ToString()));
        }

        private void removeElements(string id)
        {
            var query = from item in ents.ActualPos
                        where item.userID == id
                        select item;

            foreach (var item in query)
            {
                ents.ActualPos.DeleteObject(item);
            }

            ents.SaveChanges();
        }

        public void addNewLine(List<StationListItem> stations, int VNumber, string vType)
        {
            Vehicle v = new Vehicle();
            v.cityId = 1;
            v.vtype = vType;
            v.number = VNumber;
            
            ents.Vehicles.AddObject(v);
            ents.SaveChanges();

            foreach (var item in stations)
            {
                Station st;

                var query = from qitem in ents.Stations
                            where qitem.name == item.StationName
                            select qitem;

                if (query.Count() == 0)
                {
                    st = new Station();
                    st.name = item.StationName;
                    st.latitude = item.Latitude;
                    st.longitude = item.Longiture;
                    ents.Stations.AddObject(st);
                   ents.SaveChanges();
                }
                else
                {
                    st = query.First();                 
                }
               

                Travel tr = new Travel();
                tr.stationId = st.id;
                tr.Stationnumber = item.Snumber;
                tr.vehicleId = v.id;
                tr.timeFromTheLastInSeconds = 0;

                ents.Travels.AddObject(tr);

                ents.SaveChanges();
            }

        }

    }    

}
